Skip to content

feat!: Use a radix trie for route matching - #628

Open
ElijahAhianyo wants to merge 35 commits into
masterfrom
elijah/router-trie
Open

feat!: Use a radix trie for route matching#628
ElijahAhianyo wants to merge 35 commits into
masterfrom
elijah/router-trie

Conversation

@ElijahAhianyo

@ElijahAhianyo ElijahAhianyo commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Description

The current router implementation uses a Vec to store routers, which has some real limitations. For example, using a vec meant route conflict detection was cumbersome and hacky to get right. Using a Radix trie is the right data structure for this problem. This PR delegates the core Trie logic to the matchit crate. We still keep our business logic in a light wrapper over the Matchit Router.

Breaking Changes

  1. Registering a route with a duplicate parameter at the same segment will now fail
Router::with_urls([
    Route::with_handler_and_name("/foo/{bar}", index, "index"),
        Route::with_handler_and_name("/foo/{baz}", add_example_form, "add"),
])
  1. When a handler and router are registered for the same route, the handler will take precedence at lookup time
let nested_router = Router::with_urls([Route::with_handler_and_name(
        "/inner/{id}",
        nested,
       "nested",
)]);

Router::with_urls([
    Route::with_handler_and_name("/foo", index, "index"),
    Route::with_router("/foo", nested_router),
])
  1. A route is treated as different from its trailing-slash counterpart:
Router::with_urls([
    Route::with_handler_and_name("/foo", index, "index"),
    Route::with_handler_and_name("/foo/", another_handler, "another" ),
])
  1. Routers with the same mount point or path will be merged into one
Router::with_urls([
    Route::with_router("/foo", nested_router_a),
    Route::with_router("/foo/", nested_router_b ),
])

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / cleanup
  • Performance improvement
  • Other (describe above)

@github-actions github-actions Bot added the C-lib Crate: cot (main library crate) label Aug 1, 2026
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

🐰 Bencher Report

Projectcot
Branchelijah/router-trie
Testbedgithub-ubuntu-latest
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
milliseconds (ms)
(Result Δ%)
Upper Boundary
milliseconds (ms)
(Limit %)
empty_router/empty_router📈 view plot
🚷 view threshold
14.44 ms
(+52.66%)Baseline: 9.46 ms
17.95 ms
(80.44%)
json_api/json_api📈 view plot
🚷 view threshold
1.14 ms
(+10.42%)Baseline: 1.03 ms
1.36 ms
(83.49%)
nested_routers/nested_routers📈 view plot
🚷 view threshold
1.09 ms
(+12.89%)Baseline: 0.96 ms
1.25 ms
(86.83%)
single_root_route/single_root_route📈 view plot
🚷 view threshold
1.02 ms
(+10.40%)Baseline: 0.93 ms
1.22 ms
(84.20%)
single_root_route_burst/single_root_route_burst📈 view plot
🚷 view threshold
17.43 ms
(+3.61%)Baseline: 16.82 ms
21.43 ms
(81.30%)
🐰 View full continuous benchmarking report in Bencher

@github-actions github-actions Bot added the A-deps Area: Dependencies label Aug 17, 2026
@ElijahAhianyo

Copy link
Copy Markdown
Contributor Author

Should be rebased on and merged after #586

@ElijahAhianyo ElijahAhianyo changed the title Use a radix trie for route matching feat!: Use a radix trie for route matching Aug 18, 2026
@ElijahAhianyo
ElijahAhianyo marked this pull request as ready for review August 25, 2026 04:22
@ElijahAhianyo
ElijahAhianyo requested a review from a team August 25, 2026 20:30
Comment thread cot/tests/project.rs Outdated
fn register_apps(&self, apps: &mut AppBuilder, _context: &RegisterAppsContext) {
apps.register_with_views(App1, "");
apps.register_with_views(App2, "");
apps.register_with_views(App2, "/foo");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any way of registering multiple routers at the same URL? This is an important feature - people might want to merge different routers at the same base URL.

If matchit doesn't let us do that because it detects a conflict, we should merge the routers. I'm not sure if we should have separate methods to do that explicitly, or keep the implicit behavior - I'll let you decide.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ended up going towards the implicit route. When two routers share the same path, we implicitly merge them into one right before we build the route lookup table and trie

Comment thread cot/src/router/path.rs Outdated
Comment thread cot/src/router.rs Outdated
/// Panics when a url string could not be parsed into a [`Route`]
#[must_use]
pub fn with_urls<T: Into<Vec<Route>>>(urls: T) -> Self {
match Self::try_with_urls(urls) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use expect here?

Comment thread cot/tests/admin.rs Outdated
Comment thread cot/src/openapi.rs Outdated
@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.79673% with 31 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cot/src/router/tree.rs 93.58% 6 Missing and 11 partials ⚠️
cot/src/router.rs 99.37% 3 Missing and 3 partials ⚠️
cot/src/error_page.rs 92.45% 3 Missing and 1 partial ⚠️
cot/src/router/path.rs 96.87% 3 Missing and 1 partial ⚠️
Flag Coverage Δ
rust 90.50% <97.79%> (+0.37%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
cot/src/openapi.rs 95.60% <ø> (ø)
cot/src/error_page.rs 92.10% <92.45%> (+0.37%) ⬆️
cot/src/router/path.rs 98.52% <96.87%> (-0.89%) ⬇️
cot/src/router.rs 97.08% <99.37%> (+4.86%) ⬆️
cot/src/router/tree.rs 93.58% <93.58%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ElijahAhianyo
ElijahAhianyo requested a review from m4tx September 4, 2026 22:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-deps Area: Dependencies C-lib Crate: cot (main library crate)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants